# @author: Samuel Taylor <samtaylor.uk@gmail.com>
# @package: winecellar
# @license: GNU GPLv3
#
# -*- coding: utf-8 -*-
#
# Copyright (C) 2010  Samuel Taylor <samtaylor.uk@gmail.com>
#----------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#-----------------------------------------------------------------------

__author__ = 'Samuel Taylor <samtaylor.uk@gmail.com>'

import os
import urllib
import tarfile
import re
import sys
import shutil
import time
import tempfile
from lxml import etree
import WineCellar.Config
from WineCellar.Bottle import Bottle
from WineCellar.Download import Download
import WineCellar.helpers
from WineCellar.Logger import logger

class ApplicationPack():
    def __init__(self, pack, shortName, bottle = None):
        self.packPath = pack
        self.packShortName = shortName
        self.shortName = shortName
        self.tempPath = tempfile.mkdtemp() 
        
        self.extract()
        self.applicationPackXmlParser()
        self.name = self.tree.find("name").text
        self.installerData = self.installParser()
        if bottle == None:
            self.installerData["bottle"] = shortName
        else:
            self.installerData["bottle"] = bottle

    def setBottle(self, bottle):
        self.installerData["bottle"] = bottle
        
    def applicationPackXmlParser(self):
        parser = etree.XMLParser(remove_blank_text=True)
        self.tree = etree.parse(str(self.tempPath + "/control.xml"), parser)

    def installParser(self):
        """
        Parse the data for installer.
        """
        
        installData = {}
        
        rootElement = False
        
        if not self.tree.find("cdrom") == None:
            for element in self.tree.iter("cdrom"):
                if element.attrib["type"] == "md5":
                    cdPath = helpers.getCD()
                    if cdPath != False:
                        if helpers.md5File(cdPath+"/"+element.attrib["check"]) == element.attrib["value"]:
                            rootElement = element
        
        if rootElement == False:
            rootElement = self.tree
        
        #Get run
        installData["run"] = rootElement.find("run").text
        
        #Get Crack if needed
        if self.isCrackRequired():
            installData["crack"] = {"file": rootElement.find("crack").text, "dest": rootElement.find("crack").attrib["dest"]}
        
        #Get download urls
        installData["downloads"] = {}
        i = 0
        for element in rootElement.iter("download"):
            installData["downloads"][i] = element.text
            i += 1
        
        #Get Regedit
        installData["regedit"] = {}
        i = 0
        for element in rootElement.iter("regedit"):
            installData["regedit"][i] = element.text
            i += 1

        #get preinstall commands
        installData["preinstall"] = {}
        i = 0
        for element in rootElement.iter("preinstall"):
            if "arg" in element.attrib:
                arg = element.attrib['arg']
            else:
                arg = None
            installData["preinstall"][i] = {"command" : element.text, "arg" : arg}
            i += 1
            
        #get install command
        installData["install"] = {}
        i = 0
        for element in rootElement.iter("install"):
            if "arg" in element.attrib:
                arg = element.attrib['arg']
            else:
                arg = None
            installData["install"][i] = {"command" : element.text, "arg" : arg}
            i += 1
        
        #get postinstall commands
        installData["postinstall"] = {}
        i = 0
        for element in rootElement.iter("postinstall"):
            if "arg" in element.attrib:
                arg = element.attrib['arg']
            else:
                arg = None
            installData["postinstall"][i] = {"command" : element.text, "arg" : arg}
            i += 1
        
        return installData

    def extract(self):
        cwd = os.getcwd()
        os.chdir(self.tempPath)
        logger.debug("Extracting tarball:" + self.packPath)
        try:
            tar = tarfile.open(self.packPath, "r")
            files = tar.getmembers()
            for item in files:
                tar.extract(item)
        except:
            logger.error("Unable to extract \"" + self.packPath + "\"")
        os.chdir(cwd)
        
    def extractData(self):
        cwd = os.getcwd()
        os.makedirs(self.tempPath+"/data")
        os.chdir(self.tempPath+"/data")
        logger.debug("Extracting tarball:" + self.tempPath+"/data.tar.bz2")
        try:
            tar = tarfile.open(self.tempPath+"/data.tar.bz2", "r")
	    files = tar.getmembers()
	    for item in files:
        	tar.extract(item)
        except:
            logger.error("Unable to extract \"" + self.tempPath+"/data.tar.bz2" + "\"")
            print "Error"
        os.chdir(cwd)
        
    def download(self, updateHook = None, updateDownloadHook = None):
        downloadTempPath = str(Config.__bottlepath__ + self.installerData["bottle"] + "/drive_c/tmp")
        if not os.path.exists(downloadTempPath):
            os.makedirs(downloadTempPath)
            
        i = 1
        for key, url in self.installerData["downloads"].iteritems():
            logger.debug("Downloading file "+str(i)+" of "+str(len(self.installerData["downloads"])))
            if not updateHook == None:
                updateHook(i, len(self.installerData["downloads"]))
            logger.debug("Downloading file:" + url)
            Download(url, downloadTempPath, updateDownloadHook)
            i += 1
        
    def install(self, mode):
        for key, command in self.installerData[mode].iteritems():
            code = self.runScript(command["command"], command["arg"])
            #if code != 0:
            #    return code
    
    def installCrack(self):
        logger.debug("Copying Crack file: "+str(self.tempPath+"/data/"+self.installerData["crack"]["file"])+" dest: "+self.installerData["bottlePath"]+"/drive_c/"+self.installerData["crack"]["dest"])
        code = shutil.copy(str(self.tempPath+"/data/"+self.installerData["crack"]["file"]), self.installerData["bottlePath"]+"/drive_c/"+self.installerData["crack"]["dest"])
        logger.debug("Crack Copy return code: "+str(code))
            
    def getDependences(self):
        depends = {}
        i = 0
        for element in self.tree.iter("depends"):
            #TODO: Add Version
            depends[i] = {"shortname" : element.attrib["name"]}
            i += 1

        return depends
        
    def regEdit(self):
        cwd = os.getcwd()
        os.chdir(self.tempPath+"/data")
        
        for key, path in self.installerData["regedit"].iteritems():
            code = Bottle(self.installerData["bottle"]).run("regedit", path)
            logger.debug("Importing Reg: "+path+" in bottle: "+self.installerData["bottle"]+" Return code: "+str(code))
            if code != 0:
                os.chdir(cwd)
                return code
        os.chdir(cwd)
        return 0

    def installer(self):
        self.installerData["bottlePath"] = Config.__bottlepath__ + self.installerData["bottle"]
        
        self.extractData()
        
        self.regEdit()
        
        if not self.installerData["preinstall"] == None:
            code = self.install("preinstall")
            #if code != 0:
            #    return code
                
        if not self.installerData["install"] == None:
            code = self.install("install")
            #if code != 0:
            #    return code
                
        
        if not self.installerData["postinstall"] == None:
            code = self.install("postinstall")
            #if code != 0:
            #    return code
        
        if not self.tree.find("desktop") == None:
            self.installDesktopFile()
        
        if self.isCrackRequired():
            self.installCrack()
        
        Bottle(self.installerData["bottle"]).addInstalled(self.tree.find("shortname").text, self.tree.find("version").text)
        
        return True
    
    def runScript(self, command, arg):
        cwd = os.getcwd()
        os.chdir(self.tempPath+"/data")
        
        if re.search("\.msi", command) or re.search("\.exe", command):
            code = Bottle(self.installerData["bottle"]).run(command, arg)
        else:
            command = file(command).readlines()[0].split("#!")[1].replace("\n", " " + command)
            
            default = ""
            
            run = "PFWIN=\"" + default + "\" " + \
                  "PFUNIX=\"" + default + "\" " + \
                  "WDPREFIX=\"" + default + "\" " + \
                  "WINEPREFIX=\"" + self.installerData["bottlePath"] + "\" " + \
                  "WINEDEBUG=-all" + \
                  "WINE=\"" + default + "\" " + \
                  command + " "+\
                  self.installerData["bottlePath"] #arg
        
            code = os.system(run)
            
        os.chdir(cwd)
        return code
            
    def installDesktopFile(self):
        desktopPath = os.path.expanduser("~/.local/share/applications/")
        if not os.path.isdir(desktopPath):
            os.makedirs(desktopPath)
        
        command = self.installerData["run"]
        #print re.search("\.exe", command)
        #if re.search("\.exe", command):
        run = "env WINEDEBUG=-all WINEPREFIX="+self.installerData["bottlePath"]+" wine \""+command+"\""
        #else:
        #    run = command
        
        desktop = open(str(self.tempPath + "/data/" +  self.tree.find("desktop").text), 'a')
        desktop.write('Exec=' + run+"\n")

        if self.tree.find("category").text:            
            desktop.write('Category=' + self.tree.find("category").text+"\n")
        if self.tree.find("icon").text:
            desktop.write('Icon=' + desktopPath + self.tree.find("icon").text+"\n")
            iconName = os.path.basename(str(self.tempPath + "/data/" +  self.tree.find("icon").text))
            shutil.copy(str(self.tempPath + "/data/" + self.tree.find("icon").text), desktopPath + iconName)
        desktop.close()
        
        desktopFileName = os.path.basename(str(self.tempPath + "/data/" +  self.tree.find("desktop").text))
        shutil.copy(str(self.tempPath + "/data/" +  self.tree.find("desktop").text), desktopPath + desktopFileName)
        
    def cdCheck(self):
        logger.debug("CD Path: "+str(helpers.getCD()))
        if not self.tree.find("cdrom") == None:
            for element in self.tree.iter("cdrom"):
                if element.attrib["type"] == "md5":
                    cdPath = helpers.getCD()
                    if cdPath != False:
                        if helpers.md5File(cdPath+"/"+element.attrib["check"]) == element.attrib["value"]:
                            self.installerData = dict(self.installerData.items() + self.installParser().items())    
                            return True
        else:
            return True
        
        return False
    
    def isCrackRequired(self):
        if not self.tree.find("cdrom") == None:        
            if not self.tree.find("cdrom").find("crack") == None or not self.tree.find("crack") == None:
                return True
            else:
                return False
        elif not self.tree.find("crack") == None:
            return True
        else:
            return False
